
// ===============================================================================================================
// -*- C++ -*-
//
// Weapon.hpp - Weapon base class and weapons related interfaces.
//
// Copyright (c) 2011 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#ifndef __WEAPON_HPP__
#define __WEAPON_HPP__

// Local includes
#include <Math.hpp>
#include <Vector.hpp>
#include <Doom 3 MD5.hpp>
#include <FmodSystem.hpp>

///
/// Weapon -- Base class for the game weapons.
///
class Weapon {

public:

	// Public Interface:

	/// Fires the weapon. This method should play the weapon fire animation and sound.
	virtual void Fire(void) = 0;

	/// Reload the weapon. This method should play the weapon reload animation and sound.
	/// If the weapon has any clips left, decrements the number of clips by 1, else play the empty weapon sound and animation.
	virtual void Reload(void) = 0;

	/// Add one ammo clip to the weapon.
	virtual void AddAmmoClip(void) = 0;

	/// Add an arbitrary amount of ammo to the weapon.
	virtual void AddAmmo(int ammo) = 0;

	/// Total number of bullets.
	virtual int TotalAmmo(void) const = 0;

	/// Update the animation timers and sounds. Must be called every frame.
	virtual void Update(float elapsedTime) = 0;

	/// Check if the gun just fired.
	virtual bool HasFired(void) const = 0;

	// Data Access:

	inline int NumClips(void) const { return (numClips); };

	inline int BulletsInCurrClip(void) const { return (bulletsInClip); };

	inline const DoomMD5Model * GetModel(void) const { return (model); };

	inline const std::string & GetName(void) const { return (name); };

	virtual ~Weapon(void)
	{
		// Release all references:

		if (model) model->Release();
		if (sndFire) sndFire->Release();
		if (sndEmpty) sndEmpty->Release();
		if (sndReload) sndReload->Release();
	}

protected:

	DoomMD5Model * model; ///< The weapon mesh.
	std::string name;     ///< Weapon name.

	int numClips;         ///< Number of ammo clips. Each clip has 2 bullets.
	int bulletsInClip;    ///< Bullets in the current clip, either 2 or 0.

	// Weapon Sounds:

	FMOD::Sound * sndFire;   ///< Sound played when the gun fires.
	FMOD::Sound * sndEmpty;  ///< Sound played when the gun is empty.
	FMOD::Sound * sndReload; ///< Sound played when the gun reloads.

	Weapon(void) : numClips(0), bulletsInClip(0), sndFire(0), sndEmpty(0), sndReload(0) { /**/ }
};

// ===============================================================================================================

///
/// DoublebarrelShotgun -- Implementation of the Weapon class.
/// This is the original doublebarrel shotgun from Doom 3.
///
class DoublebarrelShotgun : public Weapon {

public:

	explicit DoublebarrelShotgun(int shells) throw (std::runtime_error);

	// Weapon Methods:

	virtual void Fire(void);
	virtual void Reload(void);
	virtual void AddAmmoClip(void);
	virtual void AddAmmo(int ammo);
	virtual int TotalAmmo(void) const;
	virtual void Update(float elapsedTime);
	virtual bool HasFired(void) const;

private:

	std::string animName; ///< Current animation name.
};

// ===============================================================================================================

///
/// AmmoBox -- A box of shells for the doublebarrel shotgun.
/// The constructor allows for many customizations.
///
class AmmoBox {

public:

	// Public Interface:

	bool pickedUp; ///< If the player picked this box up already.

	inline AmmoBox(void) { /* Do Nothing */ }

	inline AmmoBox(int ammo, GLuint listID, const Vec3 & position, const Texture * tex)
		: bullets(ammo), displayID(listID), pos(position), texture(tex), pickedUp(false)
	{
		if (texture)
		{
			texture->AddRef();
		}
	}

	inline AmmoBox & operator = (const AmmoBox & rhs)
	{
		pickedUp = rhs.pickedUp;
		bullets = rhs.bullets;
		displayID = rhs.displayID;

		pos.x = rhs.pos.x;
		pos.y = rhs.pos.y;
		pos.z = rhs.pos.z;

		texture = rhs.texture;
		return (*this);
	}

	/// Render the ammo box at its world position.
	inline void Render(void) const
	{
		glPushMatrix();

		glTranslatef(pos.x, pos.y, pos.z);

		if (texture)
		{
			texture->Bind();
		}

		glCallList(displayID);

		glPopMatrix();
	}

	// Data Access:

	inline int NumOfBullets(void) const { return (bullets); };

	inline Vec3 & Position(void) { return (pos); /* Editable */ };

private:

	int bullets; ///< Number of bullets in the ammo box.
	GLuint displayID; ///< Display list ID for the box geometry.

	Vec3 pos; ///< World position of the box.
	RefPtr<const Texture> texture; ///< Box texture, if any.
};

#endif // __WEAPON_HPP__